﻿using System.Data;

namespace PmSoft.Data.Abstractions;

public static class UnitOfWorkExtensions
{
	/// <summary>
	/// 在事务中执行同步操作
	/// </summary>
	public static void ExecuteInTransaction(
		this IUnitOfWork unitOfWork,
		Action operation,
		IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
	{
		if (unitOfWork == null)
			throw new ArgumentNullException(nameof(unitOfWork));
		if (operation == null)
			throw new ArgumentNullException(nameof(operation));

		try
		{
			unitOfWork.BeginTransaction(isolationLevel);
			operation();
			unitOfWork.CommitTransaction();
		}
		catch
		{
			unitOfWork.RollbackTransaction();
			throw;
		}
	}

	/// <summary>
	/// 在事务中执行异步操作
	/// </summary>
	public static async Task ExecuteInTransactionAsync(
		this IUnitOfWork unitOfWork,
		Func<Task> operation,
		IsolationLevel isolationLevel = IsolationLevel.ReadCommitted,
		CancellationToken cancellationToken = default)
	{
		if (unitOfWork == null)
			throw new ArgumentNullException(nameof(unitOfWork));
		if (operation == null)
			throw new ArgumentNullException(nameof(operation));

		try
		{
			await unitOfWork.BeginTransactionAsync(isolationLevel, cancellationToken)
				.ConfigureAwait(false);

			await operation().ConfigureAwait(false);

			await unitOfWork.CommitTransactionAsync(cancellationToken)
				.ConfigureAwait(false);
		}
		catch
		{
			await unitOfWork.RollbackTransactionAsync(cancellationToken)
				.ConfigureAwait(false);
			throw;
		}
	}

	/// <summary>
	/// 在事务中执行同步操作并返回结果
	/// </summary>
	public static TResult ExecuteInTransaction<TResult>(
		this IUnitOfWork unitOfWork,
		Func<TResult> operation,
		IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
	{
		if (unitOfWork == null)
			throw new ArgumentNullException(nameof(unitOfWork));
		if (operation == null)
			throw new ArgumentNullException(nameof(operation));

		try
		{
			unitOfWork.BeginTransaction(isolationLevel);
			var result = operation();
			unitOfWork.CommitTransaction();
			return result;
		}
		catch
		{
			unitOfWork.RollbackTransaction();
			throw;
		}
	}

	/// <summary>
	/// 在事务中执行异步操作并返回结果
	/// </summary>
	public static async Task<TResult> ExecuteInTransactionAsync<TResult>(
		this IUnitOfWork unitOfWork,
		Func<Task<TResult>> operation,
		IsolationLevel isolationLevel = IsolationLevel.ReadCommitted,
		CancellationToken cancellationToken = default)
	{
		if (unitOfWork == null)
			throw new ArgumentNullException(nameof(unitOfWork));
		if (operation == null)
			throw new ArgumentNullException(nameof(operation));

		try
		{
			await unitOfWork.BeginTransactionAsync(isolationLevel, cancellationToken)
				.ConfigureAwait(false);

			var result = await operation().ConfigureAwait(false);

			await unitOfWork.CommitTransactionAsync(cancellationToken)
				.ConfigureAwait(false);

			return result;
		}
		catch
		{
			await unitOfWork.RollbackTransactionAsync(cancellationToken)
				.ConfigureAwait(false);
			throw;
		}
	}
}